package com.mingben.betplatform.api.baoge3;

import java.util.Properties;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.mingben.betplatform.api.pk6758.BetStringFactory;
import com.mingben.betplatform.dto.baoge3.*;
import com.mingben.betplatform.dto.pk6758.OpenHistory;
import com.mingben.betplatform.enums.MoneyUnit;
import com.mingben.betplatform.exception.*;
import com.mingben.betplatform.util.*;
import com.mingben.betplatform.util.feifei.FeiFeiApi;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.cookie.BasicClientCookie;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.mingben.betplatform.main.Baoge3Main;
import java.io.*;
import java.time.LocalTime;
import java.time.temporal.ChronoField;
import java.util.*;
import java.util.stream.Collectors;

/**
 * official url : http://m.baoge3.com
 * 包含登錄 下注接口
 */
public class Baoge3Api {
    private Logger logger = LoggerFactory.getLogger(Baoge3Api.class);
    private String username;
    private String password;
    private List<Header> headers = new ArrayList<>();
    //http client cookie
    private CookieStore cookieStore = new BasicCookieStore();
    //http client
    private HttpClient client = HttpClients.custom().setDefaultHeaders(headers).setDefaultCookieStore(cookieStore).build();

    private Properties configProperties = new Properties();
    private String xlsxPath = "D:\\大数据定位2.2.3.xlsx";


    //已下注期号 不再下注
    private Map<String , Boolean> bettedMap = new HashMap<>();
    //文件是否修改缓存
    private Map<Long , Boolean> fileModifiedMap = new HashMap<>();
    public Baoge3Api(Properties properties){
        this.configProperties = properties;
        this.username = configProperties.get("user").toString();
        this.password = configProperties.get("pass").toString();
    }

    public void login(){
        //登錄前 獲取綁定的session
        String yiLouNumber = configProperties.get("yilou").toString();
        if(yiLouNumber.equals("")){
            logger.error("config file yilou value is null , file {} key {} " , Baoge3Main.configPath ,   "yilou");
            return;
        }


        ResultDto<VvCookieDto> vvCookieResult = retriverSession();

        String captcha =  retriveCaptcha(vvCookieResult.getData().getVvccookie()  , String.valueOf(vvCookieResult.getData().getVerifyno()));

        //登錄
        String jwtToken = doLogin(captcha , this.password , this.username , vvCookieResult.getData().getVvccookie() );
        //将jwtToken放到头上
        headers.add(new BasicHeader("token",jwtToken));

        //打印余额

        balance();


        //下注倍数  默认执行7把
        List<String> times =Arrays.asList(configProperties.getOrDefault("betTime" ,"1,1,1,1,1,1,1").toString().split(","));
        while(times.size() != 0){
            //休眠十五秒 再读取
            ThreadUtil.sleep10s();

            //修改缓存 避免多次读取 浪费内存
            File fileWatch = new File(xlsxPath);
            //文件未变动
            if(fileModifiedMap.containsKey(fileWatch.lastModified())){
                logger.info("file not change  ,,  wait dashuju collect update ,," );
                continue;
            }else{
                fileModifiedMap.put(fileWatch.lastModified() , Boolean.TRUE);
            }

            //防止文件被别人使用 锁住 等待1秒 持续十次失败
            List<String> betValue = null;
            try {
                for(int x = 0 ; x< 10;x++){
                    Workbook workbook = ExcelUtil.getWorkbok(fileWatch);
                    SearchStrategy searchStrategy = new SearchStrategy(Integer.valueOf(yiLouNumber) , workbook);
                    betValue = searchStrategy.searchBetValue();

                    if(betValue != null && betValue.size() != 0){
                        System.out.println("");
                        break;
                    }
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            }catch (IOException e){
                logger.error("excel file has been in use ,,");
            }
            if(betValue == null || betValue.equals("") || "||||".equals(betValue)){
                logger.info("can not find yilou number wait for dashuju collect ,, 15s . ");
                continue;
            }


            //獲取開獎結果
            ResultDto<BetHistory> betHistory = retriveOpenHistory();

            String today = betHistory.getData().getLastnumber().split("-")[0];
            String period = betHistory.getData().getLastnumber().split("-")[1];
            String betNumber = today + "-" + (Integer.valueOf(period) + 1);

            //缓存key是 当前期号 当前投注号码 如果这两个不同 需要再次投注 也就是允许一期里面投注两次不同的奖项
            String betKey = betNumber +"_" + betValue;
            if(bettedMap.get(betKey) != null){
                logger.info("number {} bet {}   wait dashuji collect update ,, 15s . " , betNumber , betValue);
                continue;
            }
            //准备下注 默认下注为分 li fen jiao yuan
            String unit = configProperties.getOrDefault("betUnit" , "fen" ).toString();

            if(unit.equals("fen")){
                unit = "分";
            }else if(unit.equals("jiao")){
                unit = "角";
            }else if(unit.equals("yuan")){
                unit = "元";
            }else if(unit.equals("li")){
                unit = "厘";
            }


            //是否继续执行 中奖即停
            boolean win = betWin(betHistory.getData().getLastnumber() ,betNumber , times.get(0) , MoneyUnit.getByName(unit) , betValue , betKey);

            //比对下注之后余额 与 开奖更新之后余额 若中奖 则金钱增加  若输 则相反
            if( win && configProperties.get("winStop") == null || configProperties.get("winStop").toString().equals("true")){
                //退出循环
                return;
            }
            times.remove(times.get(0));
        }


    }

    //是否下注中奖

    /**
     *
     * @param lastOpenNumber
     * @param betNumber
     * @param time
     * @param unit
     * @param betValue
     * @param betKey
     * @return
     */
    private boolean betWin(String lastOpenNumber , String betNumber, String time, MoneyUnit unit, List<String> betValue , String betKey) {
        String balanceBeforeBet = balance();
        boolean result = true;
        betMulti(betNumber ,Integer.valueOf(time) , unit,  betValue );



//        String balanceAfterBetStr = balance();

        //一直等到开奖历史更新 说明已经结算
        //獲取開獎結果
        while(!betNumber.equals(retriveOpenHistory().getData().getLastnumber())){
            logger.info(" {} has not kaijiang , wait for 5s" , betNumber);
            ThreadUtil.sleep5s();
        }
        //等待五秒 等待结算
        logger.info(" wait to settle , , ");
        ThreadUtil.sleep10s();

        String balanceAfterOpen  = balance();

        if(Double.valueOf(balanceAfterOpen) == Double.valueOf(balanceBeforeBet)){

            result = false;
        }else{
            logger.info("Congratulations win!~~  stop when win active,, exit,");
            result = true;
        }
        return result;
    }


    public String balance(){
        String url = "http://m.baoge3.com/?controller=ionapi&action=balance";
        HttpGet balanceGet = new HttpGet(url);
        try {
            HttpResponse response = client.execute(balanceGet);
            String respStr = EntityUtils.toString(response.getEntity());
            ResultDto<String> result = JSONObject.parseObject(respStr , new TypeReference<ResultDto<String>>(String.class){});
            logger.info("balance : {}" , result.getData());
            return result.getData();
        } catch (IOException e) {
            logger.error(" get balance error , {} , retry " , e.getMessage());
            ThreadUtil.sleep3s();
            return balance();
        }
    }

    /**
     * 下注
     * @param number 下注期号
     * @param times  下注倍数
     * @param unit   下注金额单位
     * @param codes  下注号码 多次下注
     * @throws BetFailureException
     */
    private boolean betMulti(String number , int times , MoneyUnit unit ,  List<String> codes ) {
        if(CollectionUtils.isNotEmpty(codes)){
            for (String code : codes){
                boolean betSuccess = bet(number , times , unit , code);
                if(betSuccess){
                    bettedMap.put(number , Boolean.TRUE);
                }
            }
        }
        return true;
    }
    /**
     * 下注
     * @param number 下注期号
     * @param times  下注倍数
     * @param unit   下注金额单位
     * @param codes  下注号码
     * @throws BetFailureException
     */
    private boolean bet(String number , int times , MoneyUnit unit ,  String codes ) {
        String url = "http://m.baoge3.com/?controller=ionapi&action=bet";
        HttpPost betPost = new HttpPost(url);
        betPost.setHeader("Content-Type" , "application/json;charset=utf-8application/json;charset=utf-8");
        betPost.setHeader("User-Agent" , " Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:73.0) Gecko/20100101 Firefox/73.0");
        betPost.setHeader("Referer" , "https://m.baoge3.com/");
        //cookie client自動管理
        BetProjectDto projectDto = new BetProjectDto(times , unit , codes);
        String encodeStr = SecurityUtils.encode(projectDto);
        BetDto dto = BetDto.builder().lt_issue_start(number).lt_project(Arrays.asList(encodeStr)).build();
        betPost.setEntity(new StringEntity(JSONObject.toJSONString(dto) , ContentType.APPLICATION_JSON));
        try {
            HttpResponse response = client.execute(betPost);
            String respStr = EntityUtils.toString(response.getEntity());

            ResultDto<String> result = JSONObject.parseObject( respStr , new TypeReference<ResultDto<String>>(String.class){});

            logger.info("bet resp ,{}" , respStr);
            if(result.getStatus() == 1){
                logger.info("bet success,,,");
                logger.info("number {} , times {}, unit {} , codes {}" , number , times  , unit.getName() , codes);
                return true;
            }else{
                if(result.getMsg().contains("投注当前期")){
//                    String today = number.split("-")[0];
//                    String period = number.split("-")[1];
//                    String betNumber = today + "-" + (Integer.valueOf(period) + 1);
//                    //延迟一秒下注  当前期 已经不允许投注 尝试投注下一期
//                   ThreadUtil.sleep1s();
                   logger.error("mistake current bet chance , restart , ");
//                    bet(betNumber , times , unit , codes);
                    return false;
                }
                if(result.getStatus() == -400){
                    logger.error("bet content wrong , ignore , continue , ");
                    return false;
                }
            }

        } catch (IOException e) {
            logger.error("bet fail , ioexception  , ignore , continue , ");
            return false;
//            bet(number, times, unit, codes);
//            ThreadUtil.sleep3s();
        }
        return false;
    }

    /**
     * 獲取開獎結果
     */
    private ResultDto<BetHistory> retriveOpenHistory(){

        String url = "http://m.baoge3.com/?controller=ionapi&action=getlastopened&lotteryid=3";
        HttpGet historyGet = new HttpGet(url);
        historyGet.setHeader("User-Agent" , " Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:73.0) Gecko/20100101 Firefox/73.0");
        historyGet.setHeader("Referer" , "https://m.baoge3.com/");

        try {
            HttpResponse response = client.execute(historyGet);
            String respStr = EntityUtils.toString(response.getEntity());

            ResultDto<BetHistory> betHistory =  JSONObject.parseObject(respStr , new TypeReference<ResultDto<BetHistory>>(BetHistory.class){});
            logger.info("get open history success,,");
            logger.info("open history :  " + betHistory.getData().getHistoryBall().get(0).getCode());
            return betHistory;
        } catch (IOException e) {
            logger.error(" retriveOpenHistory error , {} , retry " , e.getMessage());
            ThreadUtil.sleep3s();
            return retriveOpenHistory();
        }
    }
    /**
     * 獲取验证码
     */
    private String retriveCaptcha(String vvcookie , String verifyNo) {
        String formatUrl = "http://m.baoge3.com/?controller=default&action=image&verifyno=%s&vvccookie=%s";
        String url = String.format(formatUrl ,  verifyNo ,  vvcookie);
        HttpGet uuidGet = new HttpGet(url);
        uuidGet.setHeader("Content-Type" , "application/x-www-form-urlencoded");
        uuidGet.setHeader("User-Agent" , " Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:73.0) Gecko/20100101 Firefox/73.0");
        uuidGet.setHeader("Referer" , "http://m.baoge3.com/");
        try {
            HttpResponse response = client.execute(uuidGet);
            byte [] byteImgData = EntityUtils.toByteArray(response.getEntity());
            String result = "";

            try {
                result = new FeiFeiApi().recognize(byteImgData);
            } catch (Exception e) {
                e.printStackTrace();
                logger.error("call feifei api get captcha error...");
                return retriveCaptcha(vvcookie , verifyNo);
            }
            logger.info("get kaptcha success,, {}" , result);
            return result;
        } catch (IOException e) {
            return retriveCaptcha(vvcookie , verifyNo);
        }


    }

    /**
     * 登錄 返回jwt token头
     */
    private String doLogin(String captcha , String  password , String username , String vvCookie  ) {
        String url = "http://m.baoge3.com/?controller=ionapi&action=login";
        HttpPost loginPost = new HttpPost(url);
        loginPost.setHeader("Content-Type" , "application/json;charset=utf-8");
        //cookie client自動管理
        LoginDto loginDto = LoginDto.builder()
                .checkcode(Long.valueOf(captcha)).password(password).username(username).vvccookie(vvCookie).build();
        loginPost.setEntity(new StringEntity(JSON.toJSONString(loginDto) , ContentType.APPLICATION_JSON));
        try {
            HttpResponse response = client.execute(loginPost);
            String respStr = EntityUtils.toString(response.getEntity());
            ResultDto<TokenDto> tokenDto = JSONObject.parseObject(respStr , new TypeReference<ResultDto<TokenDto>>(TokenDto.class){});

            if(tokenDto.getStatus() == -6){
                logger.error("account be logged in other user , ");
                System.exit(0);
            }
            if(tokenDto.getData() == null){
                logger.error("login failed  , retry , {}", respStr );
                return doLogin(captcha , password , username , vvCookie);
            }
            logger.info("login success,, get token : " +tokenDto.getData().getToken());
            return tokenDto.getData().getToken();
        } catch (IOException e) {
            logger.error("login failed  , retry , ");
            return doLogin(captcha , password , username , vvCookie);
        }

    }

    /**
     * 獲取session
     * @throws SessionRetriveException
     */
    private ResultDto<VvCookieDto> retriverSession(){
        String url = "http://m.baoge3.com/?controller=ionapi&action=checkcode";
        HttpGet sessionGet = new HttpGet(url);
        sessionGet.setHeader("Content-Type" , "application/x-www-form-urlencoded");
        try {
            HttpResponse response = client.execute(sessionGet);
            String respStr = EntityUtils.toString(response.getEntity());
            System.out.println("get session respstr : " + respStr);
            ResultDto<VvCookieDto> result = JSONObject.parseObject(respStr , new TypeReference<ResultDto<VvCookieDto>>(VvCookieDto.class){});
            return result;
        } catch (IOException e) {
            ThreadUtil.sleep10s();
            logger.error("session get failed  , retry , ");
            return retriverSession();
        }
    }

}
